package com.zwj.user.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zwj.common.entity.*;
import com.zwj.common.service.DemandersInsideAndOutsideTheSchoolService;
import com.zwj.common.service.StudentService;
import com.zwj.common.service.TeacherService;
import com.zwj.common.service.UserService;
import com.zwj.user.constant.RedisKey;
import com.zwj.user.dto.TokenDTO;
import com.zwj.user.entity.Permission;
import com.zwj.user.entity.Role;
import com.zwj.user.entity.RoleAndPermission;
import com.zwj.user.entity.UserAndRole;
import com.zwj.user.security.PasswordSecurity;
import com.zwj.user.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

//@Service
@Slf4j
@SuppressWarnings("all")
@DubboService(loadbalance = RoundRobinLoadBalance.NAME)
@Service
public class UserServiceImpl implements UserService {

    @Resource
    private SuperAdministratorService superAdministratorService;

    @Resource
    private AuditAdministratorService auditAdministratorService;

    @Resource
    private SchoolAdministratorService schoolAdministratorService;

    @Resource
    private TeacherService teacherService;

    @Resource
    private StudentService studentService;

    @Autowired
    @Qualifier("demandersService")
    private DemandersInsideAndOutsideTheSchoolService demandersService;

    @Resource
    private UserAndRoleService userAndRoleService;

    @Resource
    private RoleService roleService;

    @Resource
    private RoleAndPermissionService roleAndPermissionService;

    @Resource
    private PermissionService permissionService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Object loginByPassword(User user) {

        switch (user.getUserType()) {

            case 1:
                return superAdministratorLoginByPassword(user);
            case 2:
                return auditAdministratorLoginByPassword(user);
            case 3:
                return schoolAdministratorLoginByPassword(user);
            case 4:
                return teacherLoginByPassword(user);
            case 5:
                return studentLoginByPassword(user);
            case 6:
                return demandersLoginByPassword(user);
            default:
                return "错误!";
        }
    }

    //笨办法，抽离方法
    private Object superAdministratorLoginByPassword(User user) {
        LambdaQueryWrapper<SuperAdministrator> superAdministratorWrapper =
                new LambdaQueryWrapper<>();

        superAdministratorWrapper.eq(SuperAdministrator::getUsername,
                user.getUsername());
        SuperAdministrator superAdministrator =
                superAdministratorService.getOne(superAdministratorWrapper);

        if (ObjectUtils.isEmpty(superAdministrator))
            return "尚未注册，请注册!";

        String ps = PasswordSecurity.Md5Encoding(user.getPassword());
        if (!ps.equals(superAdministrator.getPassword()))
            return "用户名或密码错误!";


        StpUtil.login(superAdministrator.getId());

        //获取权限
        LambdaQueryWrapper<UserAndRole> userAndRoleWrapper = new LambdaQueryWrapper<>();
        userAndRoleWrapper.eq(UserAndRole::getUserId, superAdministrator.getId());
        List<UserAndRole> userAndRoles = userAndRoleService.list(userAndRoleWrapper);
        Role role = roleService.getById(userAndRoles.get(0).getRoleId());
        LambdaQueryWrapper<RoleAndPermission> roleAndPermissionWrapper = new LambdaQueryWrapper<>();
        roleAndPermissionWrapper.eq(RoleAndPermission::getRoleId, role.getId());
        List<RoleAndPermission> roleAndPermissions = roleAndPermissionService.list(roleAndPermissionWrapper);

        List<String> range = stringRedisTemplate.opsForList().range(RedisKey.USER_GET_PERMISSION
                + superAdministrator.getId(), 0, roleAndPermissions.size());
        for (RoleAndPermission roleAndPermission : roleAndPermissions) {

            if (ObjectUtil.isNotEmpty(range)) break;
            Permission permission = permissionService.getById(roleAndPermission.getPermissionId());
            //权限存入redis
            stringRedisTemplate.opsForList().rightPush(RedisKey.USER_GET_PERMISSION
                    + superAdministrator.getId(), permission.getPermissionName());
        }


        return new TokenDTO(StpUtil.getTokenName(), StpUtil.getTokenValue());
    }

    //笨办法，抽离方法
    private Object auditAdministratorLoginByPassword(User user) {
        LambdaQueryWrapper<AuditAdministrator> auditAdministratorWrapper =
                new LambdaQueryWrapper<>();

        auditAdministratorWrapper.eq(AuditAdministrator::getUsername,
                user.getUsername());
        AuditAdministrator auditAdministrator =
                auditAdministratorService.getOne(auditAdministratorWrapper);

        if (ObjectUtils.isEmpty(auditAdministrator))
            return "尚未注册，请注册!";

        String ps = PasswordSecurity.Md5Encoding(user.getPassword());
        if (!ps.equals(auditAdministrator.getPassword()))
            return "用户名或密码错误!";

        if (auditAdministrator.getStatus().equals(0))
            return "你的账号已被封禁，等待解封!";

        StpUtil.login(auditAdministrator.getId());

        //获取权限
        LambdaQueryWrapper<UserAndRole> userAndRoleWrapper = new LambdaQueryWrapper<>();
        userAndRoleWrapper.eq(UserAndRole::getUserId, auditAdministrator.getId());
        List<UserAndRole> userAndRoles = userAndRoleService.list(userAndRoleWrapper);
        Role role = roleService.getById(userAndRoles.get(0).getRoleId());
        LambdaQueryWrapper<RoleAndPermission> roleAndPermissionWrapper = new LambdaQueryWrapper<>();
        roleAndPermissionWrapper.eq(RoleAndPermission::getRoleId, role.getId());
        List<RoleAndPermission> roleAndPermissions = roleAndPermissionService.list(roleAndPermissionWrapper);
        Permission permission = permissionService.getById(roleAndPermissions.get(0).getPermissionId());

        List<String> range = stringRedisTemplate.opsForList().range(RedisKey.USER_GET_PERMISSION
                + auditAdministrator.getId(), 0, roleAndPermissions.size());
        if (ObjectUtil.isEmpty(range)) {
            //权限存入redis
            stringRedisTemplate.opsForList().rightPush(RedisKey.USER_GET_PERMISSION
                    + auditAdministrator.getId(), permission.getPermissionName());
        }
        return new TokenDTO(StpUtil.getTokenName(), StpUtil.getTokenValue());
    }

    //笨办法，抽离方法
    private Object schoolAdministratorLoginByPassword(User user) {
        LambdaQueryWrapper<SchoolAdministrator> schoolAdministratorWrapper =
                new LambdaQueryWrapper<>();

        schoolAdministratorWrapper.eq(SchoolAdministrator::getUsername,
                user.getUsername());
        SchoolAdministrator schoolAdministrator =
                schoolAdministratorService.getOne(schoolAdministratorWrapper);

        if (ObjectUtils.isEmpty(schoolAdministrator))
            return "尚未注册，请注册!";

        String ps = PasswordSecurity.Md5Encoding(user.getPassword());
        if (!ps.equals(schoolAdministrator.getPassword()))
            return "用户名或密码错误!";

        if (schoolAdministrator.getStatus().equals(0))
            return "你的账号已被封禁，等待解封!";

        StpUtil.login(schoolAdministrator.getId());

        //获取权限
        LambdaQueryWrapper<UserAndRole> userAndRoleWrapper = new LambdaQueryWrapper<>();
        userAndRoleWrapper.eq(UserAndRole::getUserId, schoolAdministrator.getId());
        List<UserAndRole> userAndRoles = userAndRoleService.list(userAndRoleWrapper);
        Role role = roleService.getById(userAndRoles.get(0).getRoleId());
        LambdaQueryWrapper<RoleAndPermission> roleAndPermissionWrapper = new LambdaQueryWrapper<>();
        roleAndPermissionWrapper.eq(RoleAndPermission::getRoleId, role.getId());
        List<RoleAndPermission> roleAndPermissions = roleAndPermissionService.list(roleAndPermissionWrapper);
        Permission permission = permissionService.getById(roleAndPermissions.get(0).getPermissionId());

        List<String> range = stringRedisTemplate.opsForList().range(RedisKey.USER_GET_PERMISSION
                + schoolAdministrator.getId(), 0, roleAndPermissions.size());
        if (ObjectUtil.isEmpty(range)) {
            //权限存入redis
            stringRedisTemplate.opsForList().rightPush(RedisKey.USER_GET_PERMISSION
                    + schoolAdministrator.getId(), permission.getPermissionName());
        }
        return new TokenDTO(StpUtil.getTokenName(), StpUtil.getTokenValue());
    }

    //笨办法，抽离方法
    private Object teacherLoginByPassword(User user) {
        LambdaQueryWrapper<Teacher> teacherWrapper =
                new LambdaQueryWrapper<>();

        teacherWrapper.eq(Teacher::getUsername,
                user.getUsername());
        Teacher teacher =
                teacherService.getOne(teacherWrapper);

        if (ObjectUtils.isEmpty(teacher))
            return "尚未注册，请注册!";

        String ps = PasswordSecurity.Md5Encoding(user.getPassword());
        if (!ps.equals(teacher.getPassword()))
            return "用户名或密码错误!";

        if (teacher.getStatus().equals(0))
            return "你的账号已被封禁，等待解封!";

        StpUtil.login(teacher.getId());

        //获取权限
        LambdaQueryWrapper<UserAndRole> userAndRoleWrapper = new LambdaQueryWrapper<>();
        userAndRoleWrapper.eq(UserAndRole::getUserId, teacher.getId());
        List<UserAndRole> userAndRoles = userAndRoleService.list(userAndRoleWrapper);
        Role role = roleService.getById(userAndRoles.get(0).getRoleId());
        LambdaQueryWrapper<RoleAndPermission> roleAndPermissionWrapper = new LambdaQueryWrapper<>();
        roleAndPermissionWrapper.eq(RoleAndPermission::getRoleId, role.getId());
        List<RoleAndPermission> roleAndPermissions = roleAndPermissionService.list(roleAndPermissionWrapper);
        Permission permission = permissionService.getById(roleAndPermissions.get(0).getPermissionId());

        List<String> range = stringRedisTemplate.opsForList().range(RedisKey.USER_GET_PERMISSION
                + teacher.getId(), 0, roleAndPermissions.size());
        if (ObjectUtil.isEmpty(range)) {
            //权限存入redis
            stringRedisTemplate.opsForList().rightPush(RedisKey.USER_GET_PERMISSION
                    + teacher.getId(), permission.getPermissionName());
        }
        return new TokenDTO(StpUtil.getTokenName(), StpUtil.getTokenValue());
    }

    //笨办法，抽离方法
    private Object studentLoginByPassword(User user) {
        LambdaQueryWrapper<Student> studentWrapper =
                new LambdaQueryWrapper<>();

        studentWrapper.eq(Student::getUsername,
                user.getUsername());
        Student student =
                studentService.getOne(studentWrapper);

        if (ObjectUtils.isEmpty(student))
            return "尚未注册，请注册!";

        String ps = PasswordSecurity.Md5Encoding(user.getPassword());
        if (!ps.equals(student.getPassword()))
            return "用户名或密码错误!";

        if (student.getStatus().equals(0))
            return "你的账号已被封禁，等待解封!";

        StpUtil.login(student.getId());

        //获取权限
        LambdaQueryWrapper<UserAndRole> userAndRoleWrapper = new LambdaQueryWrapper<>();
        userAndRoleWrapper.eq(UserAndRole::getUserId, student.getId());
        List<UserAndRole> userAndRoles = userAndRoleService.list(userAndRoleWrapper);
        Role role = roleService.getById(userAndRoles.get(0).getRoleId());
        LambdaQueryWrapper<RoleAndPermission> roleAndPermissionWrapper = new LambdaQueryWrapper<>();
        roleAndPermissionWrapper.eq(RoleAndPermission::getRoleId, role.getId());
        List<RoleAndPermission> roleAndPermissions = roleAndPermissionService.list(roleAndPermissionWrapper);
        Permission permission = permissionService.getById(roleAndPermissions.get(0).getPermissionId());

        List<String> range = stringRedisTemplate.opsForList().range(RedisKey.USER_GET_PERMISSION
                + student.getId(), 0, roleAndPermissions.size());
        if (ObjectUtil.isEmpty(range)) {
            //权限存入redis
            stringRedisTemplate.opsForList().rightPush(RedisKey.USER_GET_PERMISSION
                    + student.getId(), permission.getPermissionName());
        }
        return new TokenDTO(StpUtil.getTokenName(), StpUtil.getTokenValue());
    }

    //笨办法，抽离方法
    private Object demandersLoginByPassword(User user) {
        LambdaQueryWrapper<DemandersInsideAndOutsideTheSchool> demandersWrapper =
                new LambdaQueryWrapper<>();

        demandersWrapper.eq(DemandersInsideAndOutsideTheSchool::getUsername,
                user.getUsername());
        DemandersInsideAndOutsideTheSchool demanders =
                demandersService.getOne(demandersWrapper);

        if (ObjectUtils.isEmpty(demanders))
            return "尚未注册，请注册!";

        String ps = PasswordSecurity.Md5Encoding(user.getPassword());
        if (!ps.equals(demanders.getPassword()))
            return "用户名或密码错误!";

        if (demanders.getStatus().equals(0))
            return "你的账号已被封禁，等待解封!";

        StpUtil.login(demanders.getId());

        //获取权限
        LambdaQueryWrapper<UserAndRole> userAndRoleWrapper = new LambdaQueryWrapper<>();
        userAndRoleWrapper.eq(UserAndRole::getUserId, demanders.getId());
        List<UserAndRole> userAndRoles = userAndRoleService.list(userAndRoleWrapper);
        Role role = roleService.getById(userAndRoles.get(0).getRoleId());
        LambdaQueryWrapper<RoleAndPermission> roleAndPermissionWrapper = new LambdaQueryWrapper<>();
        roleAndPermissionWrapper.eq(RoleAndPermission::getRoleId, role.getId());
        List<RoleAndPermission> roleAndPermissions = roleAndPermissionService.list(roleAndPermissionWrapper);
        Permission permission = permissionService.getById(roleAndPermissions.get(0).getPermissionId());

        List<String> range = stringRedisTemplate.opsForList().range(RedisKey.USER_GET_PERMISSION
                + demanders.getId(), 0, roleAndPermissions.size());
        if (ObjectUtil.isEmpty(range)) {
            //权限存入redis
            stringRedisTemplate.opsForList().rightPush(RedisKey.USER_GET_PERMISSION
                    + demanders.getId(), permission.getPermissionName());
        }
        return new TokenDTO(StpUtil.getTokenName(), StpUtil.getTokenValue());
    }

    @Override
    public Object loginByVerificationCode(User user) {
        switch (user.getUserType()) {

            case 1:
                return commonLoginByVerificationCode(superAdministratorService, new SuperAdministrator(), user);
            case 2:
                return commonLoginByVerificationCode(auditAdministratorService, new AuditAdministrator(), user);
            case 3:
                return commonLoginByVerificationCode(schoolAdministratorService, new SchoolAdministrator(), user);
            case 4:
                return commonLoginByVerificationCode(teacherService, new Teacher(), user);
            case 5:
                return commonLoginByVerificationCode(studentService, new Student(), user);
            case 6:
                return commonLoginByVerificationCode(demandersService, new DemandersInsideAndOutsideTheSchool(), user);
            default:
                return "错误!";
        }

    }

    //反射抽离，一个不错的选择
    private Object commonLoginByVerificationCode(Object service, Object userClass, User user) {

        QueryWrapper<Object> objectWrapper = new QueryWrapper<>();
        objectWrapper.setEntity(userClass);
        objectWrapper.eq("phone_number", user.getPhoneNumber());
        try {
            Method getOne = service.getClass().getMethod("getOne", Wrapper.class);
            Object invoke = getOne.invoke(service, objectWrapper);

            if (ObjectUtil.isEmpty(invoke))
                return "尚未注册，请注册!";

            Integer statusInt = -1;
            if (!StrUtil.equals(user.getUserType().toString(), 1 + "")) {
                Field status = invoke.getClass().getDeclaredField("status");
                status.setAccessible(true);
                statusInt = (Integer) status.get(invoke);
            }


            String vc = stringRedisTemplate.opsForValue().get(RedisKey.USER_VERIFICATION_CODE + user.getPhoneNumber());
            if (StrUtil.isBlank(vc))
                return "请发送验证码!";

            if (!StrUtil.equals(vc, user.getVerificationCode()))
                return "验证码错误!";

            if (statusInt == 0)
                return "你的账号已被封禁，等待解封";

            Field idF = invoke.getClass().getDeclaredField("id");
            idF.setAccessible(true);
            Object id = idF.get(invoke);
            StpUtil.login(id);

            //获取权限
            LambdaQueryWrapper<UserAndRole> userAndRoleWrapper = new LambdaQueryWrapper<>();
            userAndRoleWrapper.eq(UserAndRole::getUserId, id);
            List<UserAndRole> userAndRoles = userAndRoleService.list(userAndRoleWrapper);
            Role role = roleService.getById(userAndRoles.get(0).getRoleId());
            LambdaQueryWrapper<RoleAndPermission> roleAndPermissionWrapper = new LambdaQueryWrapper<>();
            roleAndPermissionWrapper.eq(RoleAndPermission::getRoleId, role.getId());
            List<RoleAndPermission> roleAndPermissions = roleAndPermissionService.list(roleAndPermissionWrapper);

            List<String> range = stringRedisTemplate.opsForList().range(RedisKey.USER_GET_PERMISSION
                    + id, 0, roleAndPermissions.size());

            for (RoleAndPermission roleAndPermission : roleAndPermissions) {

                if (ObjectUtil.isNotEmpty(range)) break;
                Permission permission = permissionService.getById(roleAndPermission.getPermissionId());
                //权限存入redis
                stringRedisTemplate.opsForList().rightPush(RedisKey.USER_GET_PERMISSION
                        + id, permission.getPermissionName());
            }

            return new TokenDTO(StpUtil.getTokenName(), StpUtil.getTokenValue());

        } catch (Exception e) {
            e.printStackTrace();
        }

        return "错误!";

    }

    @Override
    public String getVerificationCode(String phoneNumber) {


        String vc = stringRedisTemplate.opsForValue().get(RedisKey.USER_VERIFICATION_CODE + phoneNumber);

        if (StrUtil.isNotBlank(vc)) {
            log.info("验证码为vc: {}", vc);
            return vc;
        }


        String verificationCode = RandomUtil.randomNumbers(6);
        log.debug("验证码为: {}", verificationCode);

        stringRedisTemplate.opsForValue().set(RedisKey.USER_VERIFICATION_CODE
                + phoneNumber, verificationCode, 5, TimeUnit.MINUTES);

        return verificationCode;

    }

    //获取一个（当前）用户的权限
    @Override
    public List<String> getUserPermission(Object loginId) {
        Long size = stringRedisTemplate.opsForList().size(RedisKey.USER_GET_PERMISSION + loginId);
        if (ObjectUtil.isEmpty(size))
            return null;

//        assert size != null;
        return stringRedisTemplate.opsForList()
                .range(RedisKey.USER_GET_PERMISSION + loginId,
                        0, size);


    }

    @Override
    public Long getUserCount() {

        long count = auditAdministratorService.count();
        long count1 = schoolAdministratorService.count();
        long count2 = teacherService.count();
        long count3 = studentService.count();
        long count4 = demandersService.count();

        return count + count1 + count2 + count3 + count4;
    }

    @Override
    public Long getOnlineUserCount() {

        Set<String> keys = stringRedisTemplate.keys(RedisKey.SESSION);

        if (ObjectUtil.isEmpty(keys))
            return 0L;

        return keys.stream().count();
    }

    @Override
    public Long getLoginUserId(String token) {
        return Long.valueOf((String) StpUtil.getLoginIdByToken(token));
    }
}
